#include <QDebug>
#include <QTimer>
#include "DBSCAN.h"
#include "spatialgrid.h"

#include <QtMath>
#include <QMutex>
#include <QFile>

 DBSCAN:: DBSCAN( QObject *parent) : QObject(parent)
{
    colCount=360/GRID_SPAN;
    rowCount=180/GRID_SPAN;
    initiate();
}

 DBSCAN::~ DBSCAN()
{
     for(qint32 rowIndex=0;rowIndex<rowCount;rowIndex++)
     {
         for(qint32 colIndex=0;colIndex<colCount;colIndex++)
         {
             delete arrayGrid[rowIndex][colIndex];
         }
     }
     delete []arrayGrid;
}

void  DBSCAN::initiate()
{
    arrayGrid=new SpatialGrid*[180*GRIDS_PER_DEGREE][360*GRIDS_PER_DEGREE];
    for(qint32 rowIndex=0;rowIndex<rowCount;rowIndex++)
    {
        for(qint32 colIndex=0;colIndex<colCount;colIndex++)
        {
            double lowerLeftLon,lowerLeftLat;
            getLocation(rowIndex,colIndex,lowerLeftLon,lowerLeftLat);
            arrayGrid[rowIndex][colIndex]=new SpatialGrid(lowerLeftLon,lowerLeftLat);
        }
    }

    for(qint32 rowIndex=0;rowIndex<rowCount;rowIndex++)
    {
        for(qint32 colIndex=0;colIndex<colCount;colIndex++)
        {           
            qint32 westColIndex;
            if(colIndex==0)
                westColIndex=colCount-1;
            else
                westColIndex=colIndex-1;

            arrayGrid[rowIndex][colIndex]->setSurrounding
                    (arrayGrid[qMin(rowCount-1,rowIndex+1)][rowIndex==rowCount-1 ? (colCount/2+colIndex)%colCount:colIndex], //north
                   arrayGrid[qMin(rowCount-1,rowIndex+1)][rowIndex==rowCount-1?(colCount/2+(colIndex+1)%colCount)%colCount:(colIndex+1)%colCount], //northeast
                    arrayGrid[rowIndex][(colIndex+1)%colCount], //east
                    arrayGrid[qMax(0,rowIndex-1)][rowIndex==0?(colCount/2+(colIndex+1)%colCount)%colCount:(colIndex+1)%colCount], //southeast
                    arrayGrid[qMax(0,rowIndex-1)][rowIndex==0? (colCount/2+colIndex)%colCount:colIndex], //south
                    arrayGrid[qMax(0,rowIndex-1)][rowIndex==0?(colCount/2+westColIndex)%colCount:westColIndex], //southwest
                    arrayGrid[rowIndex][westColIndex], //west
                    arrayGrid[qMin(rowCount-1,rowIndex+1)][rowIndex==rowCount-1?(colCount/2+westColIndex)%colCount:westColIndex] //northwest
                    );
            if(!arrayGrid[qMin(rowCount-1,rowIndex+1)][rowIndex==rowCount-1?(colCount/2+westColIndex)%colCount:westColIndex])
            {
                qDebug()<<"Error. NULL grid. "<<qMin(rowCount-1,rowIndex+1)<<" "<<(rowIndex==rowCount-1?(colCount/2+westColIndex)%colCount:westColIndex);
            }
        }
    }
}

bool  DBSCAN::getLocation(qint32 rowIndex, qint32 colIndex,
                         double &lowerLeftLongitudeInDegree, double &lowerLeftLatidueInDegree)
{
    if(rowIndex>=rowCount||colIndex>=colCount) //超出范围
    {
        emit sigShowInfo("rowIndex or colIndex is out of range in getLocation()");
        return false;
    }
    //栅格左下角是(-180,-90)到(179,89),左下角的经纬度是所在栅格所有经纬度的最小值。
    //在同一个栅格中向一个固定方向航行时，经度和纬度都应当单调变化
    //每个栅格的范围是[ )，即左闭右开。北极点(x,90)会遗漏
    lowerLeftLongitudeInDegree=colIndex*GRID_SPAN-180;
    lowerLeftLatidueInDegree=rowIndex*GRID_SPAN-90;

    if(lowerLeftLongitudeInDegree<-180||lowerLeftLongitudeInDegree>180)
        qDebug()<<"error in getlocation"<<lowerLeftLongitudeInDegree<<colIndex<<rowIndex;


    return true;
}

bool  DBSCAN::getGridIndex(double longitudeInDegree, double latitudeInDegree,
                          qint32 &rowIndex, qint32 &colIndex)
{
    if(longitudeInDegree>180||longitudeInDegree<-180||
            latitudeInDegree>90||latitudeInDegree<-90)
    {
        emit sigShowInfo("Longitude and latitude is out of range");
        return false;
    }
    else
    {
        //每个栅格的范围是左闭右开
        rowIndex=qFloor(latitudeInDegree/GRID_SPAN)+90/GRID_SPAN;
        colIndex=qFloor(longitudeInDegree/GRID_SPAN)+180/GRID_SPAN;
        if(rowIndex>=(qint32)rowCount) //最大的一行和最大的一列特殊判断
            rowIndex=rowCount-1;
        if(colIndex>=(qint32)colCount)
            colIndex=colCount-1;
        return true;
    }
}

QList <QList<SpatialObjectBase *> >  DBSCAN::clusterSpatialObjectBase(QList<SpatialObjectBase *>
                                   listSpatialObjectBase, double eps, quint32 minPts)
{
    QList <QList<SpatialObjectBase *> > listClusters;

    QHash <quint64,SpatialObjectBase*> hashIDSPOBJ;
    while(!listSpatialObjectBase.isEmpty())
    {
        SpatialObjectBase * spatialObjBase=listSpatialObjectBase.first();
        listSpatialObjectBase.removeFirst();
        hashIDSPOBJ.insert(spatialObjBase->getObjectID(),spatialObjBase);

        qint32 rowIndex,colIndex;
        getGridIndex(spatialObjBase->getLongitude(),spatialObjBase->getLatitude(),rowIndex,colIndex);
        spatialObjBase->setSpatialGrid(arrayGrid[rowIndex][colIndex]);
        spatialObjBase->setDistance();
        arrayGrid[rowIndex][colIndex]->addOneSpatialObjectPointer(spatialObjBase);
    }
    qDebug()<<"所有空间对象已经进入栅格";

    while(!hashIDSPOBJ.isEmpty())
    {        
        QHashIterator <quint64,SpatialObjectBase*>  i(hashIDSPOBJ);
        if (i.hasNext())
        {
            i.next();
            SpatialObjectBase * spatialObjBase=i.value();
            hashIDSPOBJ.remove(spatialObjBase->getObjectID());

            QList <SpatialObjectBase*> listNearSOBJ=spatialObjBase->getNeighborObjects(eps);

            if(listNearSOBJ.size()>=minPts)
            {
                QHash <quint64,SpatialObjectBase*> hashIDNeighborSPOBJ;
                SpatialObjectBase::mergeList2Hash(listNearSOBJ,hashIDNeighborSPOBJ);
               // qDebug()<<"size of hashneighbor:"<<hashIDNeighborSPOBJ.size();
                QList<SpatialObjectBase *> cluster;
                spatialObjBase->expandCluster(spatialObjBase,hashIDNeighborSPOBJ,cluster,hashIDSPOBJ,eps,minPts);
                listClusters.append(cluster);
            }
            if(hashIDSPOBJ.size()%20==0) //有1/20的几率输出打印信息，减少打印信息的输出
                 qDebug()<<"待聚类的点数："<<hashIDSPOBJ.size();
        }
    }

    return listClusters;
}
